import log from "./dxLogger.js";
import std from "./dxStd.js";
import dxMap from "./dxMap.js";
import dxEventBus from "./dxEventBus.js";

/**
 * Screenshot Utility Module
 *
 * This module provides a high-level helper for:
 * - Storing screenshot upload configuration in a shared dxMap.
 * - Lazily starting a background screenshot upload worker (screenshotworker.js).
 * - Taking screenshots to local files.
 * - Binding a "double-click to screenshot" behavior on any UI view.
 *
 * Design:
 * - `init(options)` should be called once from the main thread (e.g. in main.js).
 * - Configuration is written into a dxMap entry, which the worker reads periodically.
 * - The worker is started at most once across the process, guarded by a shared flag in dxMap.
 *
 * Typical Usage:
 *   DxScreenshot.init({
 *     uploadUrl: "http://server/upload",
 *     autoNet: true,
 *     netType: "wifi",
 *     ssid: "MyWifi",
 *     password: "MyPassword",
 *   });
 *
 *   // Manual screenshot:
 *   DxScreenshot.screenshot(pageRoot, "home_20250101_120000.png");
 *
 *   // Per-page double-click screenshot:
 *   DxScreenshot.bindDoubleClickScreenshot(pageRoot, { prefix: "home" });
 */

const GLOBAL_TOPIC = "default";
const KEY_WORKER_INITED = "__screenshot_worker_inited__";
const KEY_WORKER_CONFIG = "__screenshot_worker_config__";
const WORKER_ID = "screenshotworker";
const TOPIC_SCREENSHOT_ENQUEUE = "__screenshot_enqueue__";

function notifyWorkerEnqueue(fileName) {
    try {
        dxEventBus.fire(TOPIC_SCREENSHOT_ENQUEUE, { fileName });
    } catch (e) {
        // If eventbus is not ready or we're not in the main thread, ignore it.
        // This should not affect the screenshot file being written successfully.
        log.warn("DxScreenshot: notify worker enqueue failed", e);
    }
}

const DxScreenshot = {
    _inited: false,
    _config: {},
    _worker: null,
    _lastShortPressedAt: 0,

    /**
     * Initializes the screenshot utility and (optionally) starts the upload worker.
     *
     * IMPORTANT:
     * - This function should be called only from the main thread (e.g. in main.js or
     *   the main UI worker initialization phase).
     * - Internally it may create a background worker using `std.Worker(...)` that
     *   will periodically scan and upload screenshots.
     *
     * @param {Object} options Configuration object.
     * @param {string} options.uploadUrl - HTTP endpoint used by the worker to upload screenshots.
     * @param {boolean} [options.autoNet=false] - If true, the worker will manage network
     *   connectivity itself using dxNetwork; if false, the application is expected to manage
     *   network connectivity elsewhere.
     * @param {"eth"|"wifi"} [options.netType] - Network type when `autoNet === true`.
     * @param {string} [options.ssid] - Wi-Fi SSID (Wi-Fi mode only).
     * @param {string} [options.password] - Wi-Fi password (Wi-Fi mode only).
     */
    init: function (options) {
        const cfg = options || {};
        this._config = cfg;
        this._inited = true;

        const map = dxMap.get(GLOBAL_TOPIC);

        // 1) Write the latest configuration into dxMap for the worker to read.
        const mergedCfg = {
            autoNet: false, // By default the worker does not manage network, only uploads.
            uploadUrl: "",
            ...cfg,
        };
        map.put(KEY_WORKER_CONFIG, mergedCfg);

        // 2) Start the worker only once, guarded by KEY_WORKER_INITED in dxMap.
        const inited = !!map.get(KEY_WORKER_INITED);
        if (!inited) {
            // Set the flag first to avoid race conditions between multiple threads.
            map.put(KEY_WORKER_INITED, true);
            try {
                // Use dxEventBus.newWorker so we can push file names to the worker directly.
                dxEventBus.newWorker(WORKER_ID, "/app/code/dxmodules/screenshotworker.js");
                this._worker = WORKER_ID;
                log.info("DxScreenshot.init: screenshotworker.js started successfully via dxEventBus");
            } catch (e) {
                // If worker creation fails, clear the flag so a future init() can retry.
                map.put(KEY_WORKER_INITED, false);
                log.error("DxScreenshot.init: failed to start screenshotworker.js", e);
            }
        } else {
            log.info("DxScreenshot.init: screenshot worker already started, skip creating");
        }
    },

    /**
     * Takes a screenshot of the given view and saves it to a local file.
     *
     * NOTE:
     * - This is a low-level helper that simply calls `view.snapshot(...)`.
     * - For a convenient "double-click to screenshot" behavior, use `bindDoubleClickScreenshot`.
     *
     * @param {object} view - UI view object that supports `.snapshot(fileName, type)`.
     * @param {string} fileName - Target file name, e.g. "home_20250101_120000.png".
     */
    screenshot: function (view, fileName) {
        if (!fileName || typeof fileName !== "string") {
            log.warn("DxScreenshot.screenshot: invalid fileName");
            return;
        }

        try {

            if (!view || typeof view.snapshot !== "function") {
                log.error("DxScreenshot.screenshot: view does not support snapshot");
                return;
            }

            // type = 1 means PNG format (see uiBase.snapshot documentation for details).
            view.snapshot(fileName, 1);
            log.info("DxScreenshot.screenshot: screenshot saved as", fileName);
            // Notify upload worker to enqueue this fileName (worker uploads from its own queue).
            notifyWorkerEnqueue(fileName);
        } catch (e) {
            log.error("DxScreenshot.screenshot: failed to take screenshot", e);
        }
    },

    /**
     * Builds a default screenshot file name with timestamp.
     *
     * Example: screenshot_20251223_101530.png
     *
     * @param {string} [prefix="screenshot"] - Optional file name prefix.
     * @returns {string} Generated file name.
     */
    buildFileName: function (prefix) {
        const d = new Date();
        const pad = (n) => String(n).padStart(2, "0");
        const base = prefix || "screenshot";
        return `${base}_${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(
            d.getDate()
        )}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}.png`;
    },

    /**
     * Binds a double-click-to-screenshot behavior to a given UI view.
     *
     * Rule:
     * - If two CLICK events are received on the same view within a certain time
     *   window (`interval`), and the time between them is greater than a small
     *   lower bound (`minInterval`), the second click is treated as a double-click
     *   and a screenshot is taken.
     *
     * This is a convenience helper mainly for debugging or temporary diagnostics.
     *
     * @param {object} view - UI object that supports `.on` and optionally `.snapshot`.
     * @param {object} [options]
     * @param {number} [options.interval=400] - Maximum interval in milliseconds between
     *   two clicks to be considered a double-click.
     * @param {string} [options.prefix="home"] - File name prefix used by `buildFileName`.
     */
    bindDoubleClickScreenshot: function (view, options = {}) {
        if (!view || typeof view.on !== "function") {
            log.warn("DxScreenshot.bindDoubleClickScreenshot: view is invalid or has no 'on' method");
            return;
        }

        const interval = typeof options.interval === "number" ? options.interval : 400;
        const minInterval = 80; // Lower bound to filter out noise from a single physical click.
        const prefix = options.prefix || "home";

        let lastTs = 0;

        // We use CLICK as a high-level abstraction of "single tap".
        // NOTE: Here `7` is the numeric code for CLICK in this environment.
        view.on(7, () => {
            const now = Date.now();
            const diff = now - lastTs;

            if (lastTs && diff >= minInterval && diff <= interval) {
                // Detected a double-click: take a screenshot of the provided view.
                lastTs = 0;
                const fileName = this.buildFileName(prefix);
                if (typeof view.snapshot !== "function") {
                    log.error("DxScreenshot.bindDoubleClickScreenshot: view does not support snapshot");
                    return;
                }
                try {
                    view.snapshot(fileName, 1);
                    log.info("DxScreenshot: double-click screenshot saved as", fileName);
                    notifyWorkerEnqueue(fileName);
                } catch (e) {
                    log.error("DxScreenshot: double-click screenshot failed", e);
                }
                return;
            }

            // First click: remember timestamp and wait for a possible second click.
            lastTs = now;
        });
    },
};

export default DxScreenshot;